<?php
declare (strict_types = 1);

namespace app\helper;
use Rhilip\Bencode\Bencode;
use think\facade\Config;
use think\facade\Log;

use app\admin\controller\Qbapi;
use app\admin\controller\Trapi;
use app\helper\CommonHelper;

/**
 * 种子
 */
final class TorrentHelper
{
    /**
     * 根据种子目录获取种子信息
     * @param string $save_path
     * @param string $download_type
     * @return array
     * [
     *   'pieces_hash_info' => [
     *      // 一个pieces_sha1->多个info_sha1
     *      'pieces_sha1_1'  => [
     *          'info_sha1_1', 
     *          'info_sha1_2', 
     *      ]
     *   ],
     *   'tracker_pieces' => [
     *      // 单tracker下的pieces_hash信息
     *      'tracker_1'   => [
     *          'pieces_hash_1', 
     *          'pieces_hash_2'
     *      ]
     *   ],
     *   'info_hash_pieces' => [
     *      // 一个info_sha1->一个pieces_sha1
     *      'info_sha1_1'  => 'pieces_sha1_1'
     *   ]
     * ]
     */
    public static function get_by_path(string $save_path, string $download_type = '1') : array
    {
        if(!is_dir($save_path)){
            throw new \Exception($save_path . "非文件夹或不存在,请检查[种子目录]映射是否正确");
        }

        $pieces_hash_list = [
            'pieces_hash_info'      =>  [],
            'tracker_pieces'        =>  [],
            'info_hash_pieces'      =>  [],
        ];
        // 默认按字母升序排列
        foreach (scandir($save_path) as $file_name) {
            if(pathinfo($file_name, PATHINFO_EXTENSION) != 'torrent'){
                continue;
            }

            $file_path = $save_path . '/' . $file_name;

            try {
                $torrent_data = file_get_contents($file_path);
                $torrent = Bencode::decode($torrent_data);
                $info = $torrent['info'];
                $pieces = $info['pieces'];
                $info_sha1 = sha1(Bencode::encode($info));
                $pieces_sha1 = sha1($pieces);
                
                // 辅种会导致同一个pieces_sha1->多个info_sha1
                $pieces_hash_list['pieces_hash_info'][$pieces_sha1][] = $info_sha1;

                $announce = self::get_announce($torrent, $file_path, $download_type);
                if($announce){
                    $tracker = CommonHelper::get_url($announce);
                    $pieces_hash_list['tracker_pieces'][$tracker][] = $pieces_sha1;
                }
                $pieces_hash_list['info_hash_pieces'][$info_sha1] = $pieces_sha1;
            } catch (\Exception $e) {
                Log::error('torrent解析异常,' . $file_name . ',' .$e->getMessage());
                continue;
            }
        }

        return $pieces_hash_list;
    }

    /**
     * qb批量获取种子信息
     * @param Qbapi $qbapi
     * @param array $info_hash_to_pieces
     * [
     *   'info_sha1_1'  => 'pieces_sha1_1'
     * ]
     * @return array
     * [
     *   'info_sha1_1'   =>  [
     *      'info_hash'  =>  'info_hash',
     *      'name'       =>  'name',
     *      'category'   =>  'category',
     *      'save_path'  =>  'save_path',
     *      'size'       =>  'size',
     *      'total_size' =>  'total_size',
     *      'tags'       => []
     *   ]
     * ]
     */
    public static function qb_getTorrentsInfo(Qbapi $qbapi, array $info_hash_to_pieces) : array
    {
        $info_list = [];
        
        $info_hash_to_pieces_group = array_chunk(array_keys($info_hash_to_pieces), 2000);
        foreach($info_hash_to_pieces_group as $group_list){
            // 读取已完成的种子
            $torrent_info_list = $qbapi->getTorrentsInfo(implode('|', $group_list), 'completed');
            if(empty($torrent_info_list) || count($torrent_info_list) == 0){
                continue;
            }

            foreach($torrent_info_list as $info){
                $info_list[$info['hash']] = [
                    'info_hash'     =>  $info['hash'],
                    'name'          =>  $info['name'],
                    // 种子的类别
                    'category'      =>  $info['category'],
                    // 存储此种子数据的路径  例：/media/downloads/tv
                    'save_path'     =>  $info['save_path'],
                    // 选择下载的大小
                    'size'          =>  $info['size'],
                    // 此种子中所有文件（包括未选择的文件）的总大小（字节）
                    'total_size'    =>  $info['total_size'],
                    // 种子的标签列表
                    'tags'          =>  !empty($info['tags'])? explode(',', $info['tags']) : [],
                    // //【只返回工作的就不太准了】第一个具有工作状态的跟踪器。如果没有跟踪器工作，则返回空字符串。
                    // 'tracker'       =>  $info['tracker']
                ];
            }
        }

        return $info_list;
    }

    /**
     * tr批量获取种子信息
     * @param Trapi $trapi
     * @param array $info_hash_to_pieces
     * [
     *   'info_sha1_1'  => 'pieces_sha1_1'
     * ]
     * @return array
     * [
     *   'info_sha1_1'   =>  [
     *      'info_hash'  =>  'info_hash',
     *      'name'       =>  'name',
     *      'category'   =>  'category',
     *      'save_path'  =>  'save_path',
     *      'size'       =>  'size',
     *      'total_size' =>  'total_size',
     *      'tags'       => []
     *   ]
     * ]
     */
    public static function tr_getTorrentsInfo(Trapi $trapi, array $info_hash_to_pieces) : array
    {
        $info_list = [];
        
        $info_hash_to_pieces_group = array_chunk(array_keys($info_hash_to_pieces), 2000);
        foreach($info_hash_to_pieces_group as $group_list){
            $torrent_info_list = $trapi->getTorrentsInfo($group_list);
            if (empty($torrent_info_list) || count($torrent_info_list['arguments']['torrents']) == 0) {
                continue;
            }

            foreach($torrent_info_list['arguments']['torrents'] as $info){
                // TODO:过滤未完成的
                $info_list[$info['hashString']] = [
                    'info_hash'     =>  $info['hashString'],
                    'name'          =>  $info['name'],
                    // tr没有类别的概念
                    'category'      =>  '',
                    // 存储此种子数据的路径  例：/media/downloads/tv
                    'save_path'     =>  $info['downloadDir'],
                    // 选择下载的大小
                    'size'          =>  $info['sizeWhenDone'],
                    // 此种子中所有文件（包括未选择的文件）的总大小（字节）
                    'total_size'    =>  $info['totalSize'],
                    // 种子的标签列表 例：['已整理', '转移做种']
                    'tags'          =>  isset($info['labels']) ? $info['labels'] : "" ,
                ];
            }
        }

        return $info_list;
    }

    /**
     * 根据站点数据分析已辅种完成的种子信息
     * @param mixed $site
     * @param array $tracker_pieces
     * [
     *   // 多个tracker
     *   'tracker_1'   => [
     *      'pieces_hash_1', 
     *      'pieces_hash_2'
     *   ]
     * ]
     * @return array
     */
    public static function get_pieces_by_site($site, array $tracker_pieces) : array
    {
        $pieces = [];

        /**
         * 为解决tracker中未包含站点名需特殊配置 
         * 例: 站点名: www.baidu.com tracker地址: tracker.有钱任性.com
         * 那么则需要在site.php中配置
         * 'tracker_name'  => [
         *      'www.baidu.com' =>  'tracker.有钱任性.com'
         * ]
         */
        $trackers = Config::get('site.tracker_name'); 
        $site_tracker = isset($trackers[$site['site_url']])? $trackers[$site['site_url']] : [];
        // 获取站点地址的特征信息
        $site_host_key = CommonHelper::get_url_host_key($site['site_url']);
        foreach ($tracker_pieces as $key => $value) 
        {
            // 在配置中的
            if(stristr($key, $site_host_key) !== false ||
               in_array($key, $site_tracker)){
                $pieces = array_merge($pieces, $value);
            }
        }

        return array_unique($pieces);
    }

    /**
     * 获取有效的种子信息
     * @param array $pieces_hash_info 种子目录下的
     * [
     *   'pieces_sha1_1'  => [
     *      'info_sha1_1', 
     *      'info_sha1_2', 
     *   ]
     * ]
     * @param array $info_list 实际在下载器中存在的
     * [
     *   'info_sha1_1'   =>  [
     *      'info_hash'  =>  'info_hash',
     *      'name'       =>  'name',
     *      'category'   =>  'category',
     *      'save_path'  =>  'save_path',
     *      'size'       =>  'size',
     *      'total_size' =>  'total_size',
     *      'tags'       => []
     *   ]
     * ]
     * @return array
     * [
     *   'pieces_sha1_1'  => [
     *      'info_sha1_1' => [
     *          // 同info_list['info_sha1_1']
     *      ]
     *   ]
     * ]
     */
    public static function get_valid_pieces_hash_info(array $pieces_hash_info, array $info_list) : array 
    {
        $pieces_hash_info_list = [];

        foreach($pieces_hash_info as $pieces_hash => $info_hashs){
            foreach($info_hashs as $info_hash){
                if(!isset($info_list[$info_hash])){
                    continue;
                }
                $pieces_hash_info_list[$pieces_hash][] = $info_list[$info_hash];
            }
        }

        return $pieces_hash_info_list;
    }

    /**
     * 解析torrent中的announce信息
     * @param mixed $torrent
     * @param string $file_path
     * @param string $download_type
     * @return ?string
     */
    private static function get_announce($torrent, string $file_path, string $download_type) : ?string
    {
        $announce = null;

        if(isset($torrent['announce'])){
            // TODO:$torrent['announce_list'][0]中读取所有的tracker
            $announce = $torrent['announce'];
        }else if($download_type == '1'){
            // qb种子的tracker信息要从.fastresume中补充
            $fastresume_path = str_replace('.torrent', '.fastresume', $file_path);
            if(file_exists($fastresume_path)){
                $fastresume_data = file_get_contents($fastresume_path);
                $fastresume = Bencode::decode($fastresume_data);
                if(isset($fastresume['trackers']) && 
                   is_array($fastresume['trackers']) &&
                   count($fastresume['trackers']) > 0 && 
                   is_array($fastresume['trackers'][0]) &&
                   count($fastresume['trackers'][0]) > 0){
                    $announce = $fastresume['trackers'][0][0];
                }
            }
        };

        return $announce;
    }
}